package com.remoter.consumer.internal;

import java.net.InetSocketAddress;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.remoter.api.configure.IConfiguration;
import com.remoter.api.configure.support.AbstractConfiguration;
import com.remoter.api.consumer.IConsumerService;
import com.remoter.api.exception.ConnectErrorException;
import com.remoter.api.extension.annotation.ExtensionName;
import com.remoter.api.extension.support.ExtensionLoader;
import com.remoter.api.transport.IClient;
import com.remoter.api.util.Final;
import com.remoter.api.util.NetUtil;
import com.remoter.consumer.internal.util.FinalConsumerInternal;

@ExtensionName("internal")
public class InternalConsumerService implements IConsumerService {
	
	private static final Logger log = LoggerFactory.getLogger(InternalConsumerService.class);
	
	private static final ConcurrentHashMap<InetSocketAddress,IClient> clients = new ConcurrentHashMap<InetSocketAddress,IClient>();
	
	private final IConfiguration configuration;
	
	public InternalConsumerService(){
		this.configuration = AbstractConfiguration.getConfiguration();
	}
	
	@Override
	public IClient connect(InetSocketAddress remote) throws ConnectErrorException {
		if(this.hasClient(remote)){
			return this.getClient(remote,false);
		}
		IClient client = ExtensionLoader.getService(IClient.class,this.configuration.getOption(Final.O_SERVER_TRANSPORT));
		try{
			client.connect(remote);
			clients.putIfAbsent(remote,client);
		}catch(Exception e){
			if(null != client){
				client.disConnect();
			}
			throw e;
		}
		log.info("connect success to :" + remote);
		return client;
	}

	@Override
	public boolean hasClient(InetSocketAddress remote){
		if(clients.containsKey(remote)){
			return true;
		}else{
			return false;
		}
	}

	@Override
	public IClient getClient(InetSocketAddress remote,boolean autoCreate){
		IClient client = null;
		if(!this.hasClient(remote) && autoCreate){
			try{
				log.debug("begin connect from :" + this.getBindSocketAddress() + " to :" + remote);
				this.connect(remote);
			}catch(ConnectErrorException e){
				log.error(e.getMessage(),e);
			}
		}
		if(this.hasClient(remote)){
			client = clients.get(remote);
		}
		return client;
	}

	@Override
	public void disConnect(InetSocketAddress remote){
		if(this.hasClient(remote)){
			IClient client = clients.get(remote);
			client.disConnect();
			clients.remove(remote);
		}
	}

	@Override
	public void destory(){
		for(Entry<InetSocketAddress,IClient> entry : clients.entrySet()){
			entry.getValue().disConnect();
			clients.remove(entry.getValue());
		}
		clients.clear();
	}

	@Override
	public InetSocketAddress getBindSocketAddress(){
		int port = this.configuration.getOption(FinalConsumerInternal.O_CONSUMER_INTERNAL_PORT);
		if(port == 0){
			port = NetUtil.getAvailablePort();
		}
		return new InetSocketAddress(this.configuration.getOption(FinalConsumerInternal.O_CONSUMER_INTERNAL_HOST),port);
	}
	
}